package com.easy.framework.security.utils;

import com.easy.framework.common.constant.I18NConstants;
import com.easy.framework.common.utils.AssertUtil;
import com.easy.framework.common.utils.I18NUtils;
import com.easy.framework.common.utils.SpringUtils;
import com.easy.framework.security.model.Dept;
import com.easy.framework.security.model.LoginUser;
import com.easy.framework.security.model.Role;
import com.easy.framework.security.service.RedisOAuth2AuthorizationService;
import lombok.experimental.UtilityClass;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 安全工具类
 *
 * @author L.cm
 */
@UtilityClass
public class SecurityUtils {

    private OAuth2AuthorizationService oauth2AuthorizationService;

    private OAuth2AuthorizationService getOAuth2AuthorizationService() {
        if (null == oauth2AuthorizationService) {
            oauth2AuthorizationService = SpringUtils.getBean(RedisOAuth2AuthorizationService.class);
        }
        return oauth2AuthorizationService;
    }

    /**
     * 获取Authentication
     */
    public Authentication getAuthentication() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication instanceof JwtAuthenticationToken) {
            JwtAuthenticationToken token = (JwtAuthenticationToken) authentication;
            OAuth2Authorization oAuth2Authorization = getOAuth2AuthorizationService().findByToken(token.getToken().getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
            AssertUtil.isNotNull(oAuth2Authorization,new CredentialsExpiredException(I18NUtils.message(I18NConstants.AUTHENTICATION_CREDENTIALS_EXPIRED)));
            return (UsernamePasswordAuthenticationToken) oAuth2Authorization.getAttributes().values().iterator().next();
        }
        return authentication;
    }


    /**
     * 获取Authentication
     */
    public Authentication getAuthentication(String token) {
        OAuth2Authorization oAuth2Authorization = getOAuth2AuthorizationService().findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
        return (UsernamePasswordAuthenticationToken) oAuth2Authorization.getAttributes().values().iterator().next();
    }


    /**
     * 获取用户
     */
    public LoginUser getUser(Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if (principal instanceof LoginUser) {
            return (LoginUser) principal;
        }
        return new LoginUser();
    }


    /**
     * 获取用户
     */
    public LoginUser getUser(String token) {
        return getUser(getAuthentication(token));
    }

    /**
     * 获取用户
     */
    public LoginUser getUser() {
        Authentication authentication = getAuthentication();
        if (authentication == null) {
            return null;
        }
        return getUser(authentication);
    }


    /**
     * 获取用户
     */
    public Long getUserId() {
        return getUser().getUserId();
    }


    /**
     * 获取用户
     */
    public String getUsername() {
        return getUser().getUsername();
    }


    /**
     * 获取用户昵称
     */
    public String getNickname() {
        return getUser().getName();
    }

    /**
     * 获取用户角色信息
     *
     * @return 角色集合
     */
    public List<Dept> getDepts() {
        return Optional.ofNullable(getUser().getDepts()).orElse(new ArrayList<>());
    }

    /**
     * 获取用户角色信息
     *
     * @return 角色集合
     */
    public List<Long> getDeptIds() {
        return getDepts().stream().map(Dept::getDeptId).collect(Collectors.toList());
    }

    /**
     * 获取用户角色信息
     *
     * @return 角色集合
     */
    public List<Role> getRoles() {
        return Optional.ofNullable(getUser().getRoles()).orElse(new ArrayList<>());
    }

    /**
     * 获取用户角色ID
     *
     * @return 角色集合
     */
    public List<Long> getRoleIds() {
        return getRoles().stream().map(Role::getRoleId).collect(Collectors.toList());
    }

    /**
     * 获取用户角色Key
     *
     * @return 角色集合
     */
    public Set<String> getRoleKeys() {
        return getRoles().stream().map(Role::getRoleKey).collect(Collectors.toSet());
    }

    /**
     * 是否为管理员
     *
     * @param id --用户ID
     * @return 结果
     */
    public boolean isAdmin(Long id) {
        return id != null && 1L == id;
    }

    /**
     * 是否为管理员
     *
     * @return 结果
     */
    public boolean isAdmin() {
        return isAdmin(getUserId());
    }

    /**
     * 获取权限
     *
     * @return
     */
    public Set<String> getPermissions() {
        Authentication authentication = getAuthentication();
        if (authentication == null) {
            return new HashSet<>();
        }
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        return authorities.stream()
                .map(GrantedAuthority::getAuthority)
                .filter(StringUtils::hasText).collect(Collectors.toSet());
    }

    /**
     * 密码算法 PasswordEncoder
     * @return
     */
    public static PasswordEncoder getPasswordEncoder() {
        return SpringUtils.getBean(PasswordEncoder.class);
    }


    /**
     * 加密密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password)
    {
        return getPasswordEncoder().encode(password);
    }

    /**
     * 比较密码是否相同
     *
     * @param rawPassword 真实密码
     * @param encodedPassword 加密后字符
     * @return 结果
     */
    public static boolean matchesPassword(CharSequence rawPassword, String encodedPassword)
    {
        return getPasswordEncoder().matches(rawPassword, encodedPassword);
    }
}
